﻿#include <iostream>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <vector>
#include <QtCore5Compat/QTextCodec>
using std::vector;
using std::shared_ptr;
/*
 * 路径比较模型
 * 它仅比较 . 与 ..
 * 当然，它还能返回是否存在上级路径
 */
class PathDotAndDotDot {
	/*
	 * 记录 . 的绝对路径
	 */
	QString dot;
	/*
	 * 记录 .. 的绝对路径
	 */
	QString dotDot;
	/*
	 * 是否存在 .. 的绝对路径
	 */
	bool isHasDotDot = false;
public:
	/*
	 * 构建一个路径比较对象
	 */
	PathDotAndDotDot( const QString &path ) {
		QDir dir( path );
		dir.makeAbsolute( );
		dot = dir.absolutePath( );
		if( dir.cdUp( ) ) {
			dotDot = dir.absolutePath( );
			isHasDotDot = true;
		}
	}

	bool hasDotDotPath( ) const {
		return isHasDotDot;
	}

	/*
	 * 如果路径为该对象的 . 或 .. ，那么返回 true，否则返回 false
	 */
	bool isPathDotAndDotDot( const QString &path ) const {
		QDir checkPath( path );
		checkPath.makeAbsolute( );
		const QString absolutePath = checkPath.absolutePath( );
		if( absolutePath == dot || (isHasDotDot && absolutePath == dotDot) )
			return true;
		return false;
	}

	/*
	 * 如果路径为该对象的  .. ，那么返回 true，否则返回 false
	 */
	bool isPathDotDot( const QString &path ) const {
		QDir checkPath( path );
		checkPath.makeAbsolute( );
		const QString absolutePath = checkPath.absolutePath( );
		if( (isHasDotDot && absolutePath == dotDot) )
			return true;
		return false;
	}

	/*
	 * 如果路径为该对象的 . 那么返回 true，否则返回 false
	 */
	bool isPathDot( const QString &path ) const {
		QDir checkPath( path );
		checkPath.makeAbsolute( );
		const QString absolutePath = checkPath.absolutePath( );
		if( absolutePath == dot )
			return true;
		return false;
	}
};

/*
 * 可用于计算数据大小，但是必须数据正常，可以调用 ComputerSize::normal() 来判断数据是否正常
 */
class ComputerSize {
	int64_t byte = 0, kbyte = 0, mbyte = 0, gbyte = 0, tbyte = 0;
	bool reComputerSize = false, isNormal = false;
	/*
	 * 减去操作
	 */
	void nativeSub( size_t value ) {
		size_t modValue = value % 1024;
		byte -= modValue;
		value = (value - modValue) / 1024;
		modValue = value % 1024;
		kbyte -= modValue;
		value = (value - modValue) / 1024;
		modValue = value % 1024;
		mbyte -= modValue;
		value = (value - modValue) / 1024;
		modValue = value % 1024;
		gbyte -= modValue;
		value = (value - modValue) / 1024;
		modValue = value % 1024;
		tbyte -= modValue;
		if( byte < 0 ) {
			byte += 1024;
			kbyte -= 1;
		}
		if( kbyte < 0 ) {
			kbyte += 1024;
			mbyte -= 1;
		}
		if( mbyte < 0 ) {
			mbyte += 1024;
			gbyte -= 1;
		}
		if( gbyte < 0 ) {
			gbyte += 1024;
			tbyte -= 1;
		}
	}

	/*
	 * 加上操作
	 */
	void nativeAdd( size_t value ) {
		size_t modValue = value % 1024;
		byte += modValue;
		value = (value - modValue) / 1024;
		modValue = value % 1024;
		kbyte += modValue;
		value = (value - modValue) / 1024;
		modValue = value % 1024;
		mbyte += modValue;
		value = (value - modValue) / 1024;
		modValue = value % 1024;
		gbyte += modValue;
		value = (value - modValue) / 1024;
		modValue = value % 1024;
		tbyte += modValue;
		if( byte > 1024 ) {
			byte -= 1024;
			kbyte += 1;
		}
		if( kbyte > 1024 ) {
			kbyte -= 1024;
			mbyte += 1;
		}
		if( mbyte > 1024 ) {
			mbyte -= 1024;
			gbyte += 1;
		}
		if( gbyte > 1024 ) {
			gbyte -= 1024;
			tbyte += 1;
		}
	}

public:
	ComputerSize( ) {
	}

	/*
	 * 减去操作
	 */
	void sub( size_t value ) {
		reComputerSize = true;
		nativeSub( value );
	}

	/*
	 * 加上操作
	 */
	void add( size_t value ) {
		reComputerSize = true;
		nativeAdd( value );
	}

	/*
	 * 数据是否正常
	 */
	bool normal( ) {
		if( isNormal )
			return isNormal;
		isNormal = tbyte >= 0 && gbyte >= 0 && mbyte >= 0 && kbyte >= 0 && byte >= 0;
		return isNormal;
	}

	size_t getByte( ) {
		if( reComputerSize ) {
			nativeSub( 0 );
			reComputerSize = false;
		}
		reComputerSize = false;
		if( !this->normal( ) )
			return 0;
		return byte + kbyte * 1024 + mbyte * 1024 * 1024 + gbyte * 1024 * 1024 * 1024 + tbyte * 1024 * 1024 * 1024 * 1024;
	}

	size_t getKBbyte( ) {
		if( reComputerSize ) {
			nativeSub( 0 );
			reComputerSize = false;
		}
		reComputerSize = false;
		if( !this->normal( ) )
			return 0;
		return kbyte + mbyte * 1024 + gbyte * 1024 * 1024 + tbyte * 1024 * 1024 * 1024;
	}

	size_t getMBbyte( ) {
		if( reComputerSize ) {
			nativeSub( 0 );
			reComputerSize = false;
		}
		reComputerSize = false;
		if( !this->normal( ) )
			return 0;
		return mbyte + gbyte * 1024 + tbyte * 1024 * 1024;
	}

	size_t getGBbyte( ) {
		if( reComputerSize ) {
			nativeSub( 0 );
			reComputerSize = false;
		}
		if( !this->normal( ) )
			return 0;
		return gbyte + tbyte * 1024;
	}

	size_t getTBbyte( ) {
		if( reComputerSize ) {
			nativeSub( 0 );
			reComputerSize = false;
		}
		if( !this->normal( ) )
			return 0;
		return tbyte;
	}

	/*
	 * 允许加上一个 byte 大小
	 */
	ComputerSize operator +( const size_t value ) {

		ComputerSize result = *this;
		if( !this->normal( ) )
			return result;
		result.add( value );
		return result;
	}

	/*
	 * 允许减去一个 byte 大小
	 */
	ComputerSize operator -( const size_t value ) {
		ComputerSize result = *this;
		if( !this->normal( ) )
			return result;
		result.sub( value );
		return result;
	}

	/*
	 * 允许加上一个 byte 大小
	 */
	ComputerSize operator +( ComputerSize &value ) {
		ComputerSize result = *this;
		if( !this->normal( ) )
			return result;
		result.add( value.getByte( ) );
		return result;
	}

	/*
	 * 允许减去一个 byte 大小
	 */
	ComputerSize operator -( ComputerSize &value ) {
		ComputerSize result = *this;
		if( !this->normal( ) )
			return result;
		result.sub( value.getByte( ) );
		return result;
	}

	/*
	 * 格式化输出
	 */
	QString toString( ) const {
		return QString( "%0 TB %1 GB %2 MB %3 KB %4 B" ).arg( tbyte ).arg( gbyte ).arg( mbyte ).arg( kbyte ).arg( byte );
	}
};

/**
 * 获取路径内所有的文件与文件夹信息
 * pathSize : 路径所有的文件的大小
 * pathContent : 格式化输出路径信息
 */
void getFileInfoList( const QDir &dirObj, ComputerSize *pathSize, QStringList *pathContent );
/*
 * 把一个qt字符串转换为对应的字符串
 */
std::string enCode( const QString &orgQtStr );

int main( int argc, char *argv[] ) {
	QCoreApplication a( argc, argv );
	// 获取遍历路径，不存在则退出
	QString runCheckPath;
	if( argc < 2 )
		runCheckPath = QDir::currentPath( );
	else
		runCheckPath = QString::fromLocal8Bit( argv[1] );
	QDir path( runCheckPath );
	
	if( !path.exists( ) )
		return -1;
	
	path.makeAbsolute( );
	const QString absolutePath = path.absolutePath( );
	std::cout << enCode( "遍历 : " + absolutePath ) << std::endl;
	ComputerSize pathSize;
	QStringList pathContent;
	getFileInfoList( path, &pathSize, &pathContent );
	std::cout << enCode( "遍历 : " + absolutePath ) << enCode( "\t完成" ) << std::endl;
	/*
	 * 写入文件
	 */
	QString writeFilePath = absolutePath + "/读我获取所有文件名.txt";
	QFile writeFile( writeFilePath );
	if( writeFile.open( QIODeviceBase::ReadWrite | QIODeviceBase::Truncate ) ) {
		QStringList sizeFrame;
		sizeFrame.append( "============\n单位统计，每个单位独立统计整个路径的占用存储 : \n" );
		sizeFrame.append( QString( "\t%0 TB\n" ).arg( pathSize.getTBbyte( ) ) );
		sizeFrame.append( QString( "\t%0 GB\n" ).arg( pathSize.getGBbyte( ) ) );
		sizeFrame.append( QString( "\t%0 MB\n" ).arg( pathSize.getMBbyte( ) ) );
		sizeFrame.append( QString( "\t%0 KB\n" ).arg( pathSize.getKBbyte( ) ) );
		sizeFrame.append( QString( "\t%0 B" ).arg( pathSize.getByte( ) ) );
		sizeFrame.append( "\n============\n" );
		QString string = "============\n统计大小 : " + pathSize.toString( ) + "\n";
		QString body = string.append( sizeFrame.join( "" ) );
		const QString writeCon = body + pathContent.join( "\n" ).append( "\n" ) + body;
		writeFile.write( writeCon.toUtf8( ) );
		writeFile.close( );
		std::cout << enCode( "写入 : " + writeFilePath + "\t完成" ) << std::endl;
		return 0;
	}
	return 1;
}

std::string enCode( const QString &orgQtStr ) {
	// 注意 toLocal8Bit( ) 的释放
	return orgQtStr.toLocal8Bit( ).toStdString( );
}

void fullFileInfos( const QFileInfo &fileInfo, const int tabCount, ComputerSize *pathSize, QStringList *pathContent );

void getFileInfoList( const QDir &dirObj, ComputerSize *pathSize, QStringList *pathContent ) {
	QFileInfoList fileInfoList = dirObj.entryInfoList( QDir::NoFilter, QDir::DirsFirst | QDir::Name );

	const PathDotAndDotDot checkPathDotAndDotDot( dirObj.absolutePath( ) );
	for( auto &info : fileInfoList )
		if( ! checkPathDotAndDotDot.isPathDotAndDotDot( info.absoluteFilePath( ) ) )
			fullFileInfos( info, 0, pathSize, pathContent );

}

void fullFileInfos( const QFileInfo &fileInfo, const int tabCount, ComputerSize *pathSize, QStringList *pathContent ) {

	QString tabChar( "" );
	for( int index = 0; index < tabCount; ++index )
		tabChar += '\t';
	if( fileInfo.isDir( ) ) {
		/*
		* 目录操作
		*/
		auto path = fileInfo.absoluteFilePath( );
		// 递归遍历
		const QDir dir = fileInfo.absoluteFilePath( );
		QFileInfoList fileInfoList = dir.entryInfoList( QDir::NoFilter, QDir::DirsFirst | QDir::Name );
		const PathDotAndDotDot checkPathDotAndDotDot( path );
		path.remove( fileInfo.absolutePath( ) ).append( '/' );
		path = tabChar + path;
		std::cout << enCode( path ) << std::endl;
		pathContent->append( path );
		for( auto &info : fileInfoList )
			if( ! checkPathDotAndDotDot.isPathDotAndDotDot( info.absoluteFilePath( ) ) )
				fullFileInfos( info, tabCount + 1, pathSize, pathContent );
	} else {
		/*
		* 文件操作
		*/
		// 录入信息
		auto path = fileInfo.absoluteFilePath( );
		path.remove( fileInfo.absolutePath( ) );
		path = tabChar + path;
		std::cout << enCode( path ) << std::endl;
		pathContent->append( path );
		if( pathSize )
			pathSize->add( fileInfo.size( ) );
	}
}
